﻿using System.Reflection;
using KinonekoSoftware.Extensions.Reflections;

#if WINDOWS

using ControlImpl = System.Windows.FrameworkElement;

#else


using ControlImpl = Avalonia.Controls.Control;

#endif
namespace KinonekoSoftware.Foundation.UI.Core
{
    public class PlatformViewLocator : IViewLocator
    {
        private readonly Dictionary<Type, MVVMAssociation> _MvvmAssociationInfo;

        public PlatformViewLocator()
        {
            _MvvmAssociationInfo = new Dictionary<Type, MVVMAssociation>();
        }

        #region InstallView

        /// <summary>
        /// 添加View与ViewModel之间的绑定信息
        /// </summary>
        /// <typeparam name="TView">View的类型</typeparam>
        /// <typeparam name="TViewModel">ViewModel的类型</typeparam>
        public void InstallView<TView, TViewModel>()
        {
            var tV  = typeof(TView);
            var tVM = typeof(TViewModel);

            var association = new MVVMAssociation
            {
                View      = tV,
                ViewModel = tVM,
            };
            _MvvmAssociationInfo.TryAdd(tV, association);
            _MvvmAssociationInfo.TryAdd(tVM, association);
        }


        /// <summary>
        /// 添加View与ViewModel之间的绑定信息
        /// </summary>
        /// <param name="tV">View的类型</param>
        /// <param name="tVM">ViewModel的类型</param>
        public void InstallView(Type tV, Type tVM)
        {
            if (tV is null ||
                tVM is null)
            {
                return;
            }

            var association = new MVVMAssociation
            {
                View      = tV,
                ViewModel = tVM,
            };
            _MvvmAssociationInfo.TryAdd(tV, association);
            _MvvmAssociationInfo.TryAdd(tVM, association);
        }

        /// <summary>
        /// 添加View与ViewModel之间的绑定信息
        /// </summary>
        /// <param name="association">View与ViewModel之间的绑定信息</param>
        public void InstallView(MVVMAssociation association)
        {
            if (association is null)
            {
                return;
            }
            _MvvmAssociationInfo.TryAdd(association.View, association);
            _MvvmAssociationInfo.TryAdd(association.ViewModel, association);
        }

        #endregion

        #region Associate

        /// <summary>
        /// 关联视图和视图模型
        /// </summary>
        /// <param name="instance">可能是视图模型，也可能是视图</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException">参数为空。</exception>
        /// <exception cref="ArgumentException">参数错误。</exception>
        public ControlImpl Associate(object instance)
        {
            //
            //
            ArgumentNullException.ThrowIfNull(instance);

            //
            //
            var t = instance.GetType();

            //
            //
            if (!_MvvmAssociationInfo.TryGetValue(t, out var association))
            {
                throw new ArgumentException("missing association");
            }

            ControlImpl control;

            if (association.ViewModel == t)
            {
                //
                //
                control = Activator.CreateInstance(association.View) as ControlImpl;


                if (control is null)
                {
                    throw new ArgumentNullException(nameof(control));
                }

                //
                // 绑定V and VM
                Bind(control, instance as IManagedLifetimeObject);
            }
            else
            {
                control = (ControlImpl)instance;

                //
                //
                var vm = Activator.CreateInstance(association.ViewModel) as IManagedLifetimeObject;


                //
                //
                ArgumentNullException.ThrowIfNull(vm);

                //
                //
                Bind(control, vm);
            }

            return control;
        }

        /// <summary>
        /// 关联视图和视图模型
        /// </summary>
        /// <param name="vm">视图模型</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException">参数为空。</exception>
        /// <exception cref="ArgumentException">参数错误。</exception>
        public TView Associate<TView, TViewModel>(TViewModel vm) where TView : ControlImpl where TViewModel : IManagedLifetimeObject
        {
            //
            //
            ArgumentNullException.ThrowIfNull(vm);

            //
            //
            var t = vm.GetType();

            //
            //
            if (!_MvvmAssociationInfo.TryGetValue(t, out var association))
            {
                throw new ArgumentException("missing association");
            }

            var control = Activator.CreateInstance(association.View) as TView;

            if (control is null)
            {
                throw new ArgumentNullException(nameof(control));
            }

            //
            // 绑定V and VM
            Bind(control, vm);

            return control;
        }

        #endregion

        #region InstallFromRegisters

        public void InstallFromRegisters()
        {
            var implType = ClassStatic.FindInterfaceImplementation<IViewModelRegister>(Assembly.GetEntryAssembly());

            if (implType is null)
            {
                return;
            }

            var list = new List<MVVMAssociation>(32);
            var impl = (IViewModelRegister)Activator.CreateInstance(implType);
            impl?.Register(list);


            foreach (var association in list)
            {
                InstallView(association);
            }
        }

        public void InstallFromRegisters(Assembly assembly)
        {
            var implType = ClassStatic.FindInterfaceImplementation<IViewModelRegister>(assembly);

            if (implType is null)
            {
                return;
            }

            var list = new List<MVVMAssociation>(32);
            var impl = (IViewModelRegister)Activator.CreateInstance(implType);
            impl?.Register(list);


            foreach (var association in list)
            {
                InstallView(association);
            }
        }

        #endregion
        
        private static void Bind(ControlImpl control, IManagedLifetimeObject vm)
        {
            control.DataContext = vm;
        }

        public IEnumerable<KeyValuePair<Type, MVVMAssociation>> Mappings => _MvvmAssociationInfo;
        
        
        public IEnumerable<Type> ViewModelMappings
        {
            get
            {
                return _MvvmAssociationInfo.Select(x => x.Value.ViewModel)
                                           .Distinct()
                                           .ToList();
            }
        }
        public IEnumerable<Type> ViewMappings
        {
            get
            {
                return _MvvmAssociationInfo.Select(x => x.Value.View)
                                           .Distinct()
                                           .ToList();
            }
        }

    }
}